package org.tloss.web.editor.form.editors;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.layout.TreeColumnLayout;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.model.WorkbenchContentProvider;
import org.eclipse.ui.model.WorkbenchLabelProvider;
import org.eclipse.ui.part.EditorPart;
import org.tloss.web.editor.Utils;
import org.tloss.web.editor.model.Form;
import org.tloss.web.editor.model.FormField;
import org.tloss.web.editor.model.Submit;
import org.tloss.web.editor.model.command.Body;

public class FormMainEditor extends EditorPart implements
		IResourceChangeListener {
	private boolean dirty = false;
	private Form form = null;
	TreeViewer treeViewer;

	public Form getForm() {
		return form;
	}

	FormPageEditor parent = null;

	public void setParent(FormPageEditor parent) {
		this.parent = parent;
	}

	@Override
	public void doSave(IProgressMonitor monitor) {
		IEditorInput editorInput = this.getEditorInput();
		IFileEditorInput fileEditorInput = (IFileEditorInput) editorInput;
		IFile file = fileEditorInput.getFile();
		
		JAXBContext context;
		try {
			context = JAXBContext.newInstance(Form.class);
			Marshaller marshaller = context.createMarshaller();
			ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
			marshaller.marshal(form, arrayOutputStream);

			file.setContents(
					new ByteArrayInputStream(arrayOutputStream.toByteArray()),
					false, true, monitor);
			this.dirty = false;
			this.firePropertyChange(IEditorPart.PROP_DIRTY);
		} catch (JAXBException e) {
			Utils.logError("Fail to save Form", e);
		} catch (CoreException e) {
			Utils.logError("Fail to save Form", e);
		}
	}

	@Override
	public void doSaveAs() {

	}

	@Override
	public void init(IEditorSite site, IEditorInput input)
			throws PartInitException {
		if (!(input instanceof IFileEditorInput)) {
			PartInitException exception = new PartInitException(
					"Input must be a file");
			Utils.logError("Input must be a file", exception);
			throw exception;
		}
		setSite(site);
		setInput(input);
		IFileEditorInput editorInput = (IFileEditorInput) input;
		IFile file = editorInput.getFile();
		JAXBContext context;
		InputStream inputStream = null;
		try {
			inputStream = file.getContents();
			if (inputStream.available() == 0) {
				form = new Form();

			} else {
				context = JAXBContext.newInstance(Form.class);
				Unmarshaller marshaller = context.createUnmarshaller();
				form = (Form) marshaller.unmarshal(inputStream);
			}
		} catch (CoreException e) {
			PartInitException exception = new PartInitException(
					"Can't load resource", e);
			Utils.logError("Can't load resource", exception);
			throw exception;
		} catch (JAXBException e) {
			PartInitException exception = new PartInitException(
					"Can't load resource", e);
			Utils.logError("Can't load resource", exception);
			throw exception;
		} catch (IOException e) {
			PartInitException exception = new PartInitException(
					"Can't load resource", e);
			Utils.logError("Can't load resource", exception);
			throw exception;
		} finally {
			try {
				if (inputStream != null) {
					inputStream.close();
				}
			} catch (IOException e) {
				Utils.logError("Can't close inputstream", e);
			}
		}
	}

	@Override
	public boolean isDirty() {

		return dirty;
	}

	@Override
	public boolean isSaveAsAllowed() {
		return false;
	}

	@Override
	public void createPartControl(final Composite parent) {
		Composite composite = new Composite(parent, SWT.NONE);
		GridLayout layout = new GridLayout();
		composite.setLayout(layout);
		layout.numColumns = 2;

		Composite treeContainer = new Composite(composite, SWT.BORDER);
		GridData gridData = new GridData();
		treeContainer.setLayoutData(gridData);
		gridData.horizontalAlignment = SWT.FILL;
		gridData.grabExcessHorizontalSpace = true;
		gridData.verticalAlignment = SWT.FILL;
		gridData.grabExcessVerticalSpace = true;
		TreeColumnLayout treeLayout = new TreeColumnLayout();
		treeContainer.setLayout(treeLayout);
		treeViewer = new TreeViewer(treeContainer, SWT.MULTI
				| SWT.FULL_SELECTION);
		Tree tree = treeViewer.getTree();
		tree.setHeaderVisible(true);
		TreeColumn keyColumn;

		keyColumn = new TreeColumn(tree, SWT.NONE);
		keyColumn.setText("Name");
		treeLayout.setColumnData(keyColumn, new ColumnWeightData(2));

		treeViewer.setContentProvider(new WorkbenchContentProvider());
		final WorkbenchLabelProvider treeLabelProvider = new WorkbenchLabelProvider();
		treeViewer.setLabelProvider(treeLabelProvider);

		TreeViewerColumn column1 = new TreeViewerColumn(treeViewer, keyColumn);

		column1.setLabelProvider(new ColumnLabelProvider() {
			public String getText(Object element) {
				return treeLabelProvider.getText(element);
			}

			public Image getImage(Object element) {

				return treeLabelProvider.getImage(element);
			}
		});

		column1.setEditingSupport(new EditingSupport(treeViewer) {
			TextCellEditor editor = null;

			protected boolean canEdit(Object element) {
				return true;
			}

			protected CellEditor getCellEditor(Object element) {
				if (editor == null) {
					Composite tree = (Composite) treeViewer.getControl();
					editor = new TextCellEditor(tree);

				}
				return editor;
			}

			protected Object getValue(Object element) {
				return treeLabelProvider.getText(element);
			}

			protected void setValue(Object element, Object value) {
				if (value != null) {
					String text = ((String) value).trim();
					if (element instanceof FormFieldElement) {
						if (!text.equals(((FormFieldElement) element)
								.getField().getName())) {
							((FormFieldElement) element).getField().setName(
									text);
							treeModified(element);
						}
					}
				}
			}
		});
		getSite().setSelectionProvider(treeViewer);

		treeViewer.setInput(new FormFieldList(form, this));

		treeViewer.setAutoExpandLevel(TreeViewer.ALL_LEVELS);

		Composite wrapper = new Composite(composite, SWT.NONE);
		RowLayout rowLayout = new RowLayout();
		wrapper.setLayout(rowLayout);
		rowLayout.type = SWT.VERTICAL;

		Button button = new Button(wrapper, SWT.NONE);
		button.setText("Add");
		Button buttonDelete = new Button(wrapper, SWT.NONE);
		buttonDelete.setText("Delete");

		buttonDelete.addMouseListener(new MouseListener() {

			@Override
			public void mouseUp(MouseEvent e) {

			}

			@Override
			public void mouseDown(MouseEvent e) {
				TreeItem[] items = treeViewer.getTree().getSelection();
				List<FormField> fields = form.getFormFields();
				for (int i = 0; i < items.length; i++) {
					fields.remove(((FormFieldElement) items[i].getData())
							.getField());
				}
				treeModified(null);

			}

			@Override
			public void mouseDoubleClick(MouseEvent e) {

			}
		});
		Button load = new Button(wrapper, SWT.NONE);
		load.setText("Load");
		load.addMouseListener(new MouseListener() {

			@Override
			public void mouseUp(MouseEvent e) {

			}

			@Override
			public void mouseDown(MouseEvent e) {
				List<FormField> fields = form.getFormFields();
				if (fields != null) {
					if (FormMainEditor.this.parent != null) {
						Submit provider;
						for (int i = 0; i < fields.size(); i++) {
							if ("submit".equals(fields.get(i).getType())) {
								CommandEditor commandEditor = new CommandEditor();
								List<Submit> submits = form.getSubmits();
								if (submits == null) {
									submits = new ArrayList<Submit>();
									form.setSubmits(submits);
								}
								provider = null;
								for (int j = 0; provider == null
										&& j < submits.size(); j++) {
									if (fields.get(i).getName()
											.equals(submits.get(j).getValue())) {
										provider = submits.get(j);
									}
								}
								if (provider == null) {
									provider = new Submit();
									provider.setBody(new Body());
									provider.setValue(fields.get(i).getName());
									submits.add(provider);
								}

								commandEditor.setForm(form, provider);
								commandEditor.setDirtyListenter(FormMainEditor.this);
								try {
									int index = FormMainEditor.this.parent
											.addPage(commandEditor,
													getEditorInput());
									FormMainEditor.this.parent.setPageText(
											index, fields.get(i).getName());
								} catch (PartInitException e1) {
									Utils.logError(
											"Error to load CommandEditor", e1);
								}

							}
						}
					}
				}

			}

			@Override
			public void mouseDoubleClick(MouseEvent e) {

			}
		});
		button.addMouseListener(new MouseListener() {

			@Override
			public void mouseUp(MouseEvent e) {

			}

			@Override
			public void mouseDown(MouseEvent e) {
				FormField field = new FormField();
				field.setName("field" + System.currentTimeMillis());

				List<FormField> fields = form.getFormFields();
				if (fields == null) {
					fields = new ArrayList<FormField>();
					form.setFormFields(fields);
				}
				fields.add(field);
				treeModified(null);

			}

			@Override
			public void mouseDoubleClick(MouseEvent e) {

			}
		});
	}

	@Override
	public void setFocus() {

	}

	public void treeModified(Object element) {
		if (element == null)
			treeViewer.refresh();
		else
			treeViewer.refresh(element);
		dirty = true;
		firePropertyChange(IEditorPart.PROP_DIRTY);

	}

	@Override
	public void resourceChanged(IResourceChangeEvent event) {
		treeModified(null);
	}
}
